﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Reflector.CodeModel;

namespace MethodSeqGenerator
{
    public static class SeqGenUtil
    {
        //Function that accepts an expression and extracts the type holder
        //based on its actual type
        public static TypeHolder ExtractTypeHolder(IExpression expr)
        {
            if (expr == null)
            {
                throw new ArgumentNullException("expression");
            }

            IVariableReferenceExpression variableReferenceExpression = expr as IVariableReferenceExpression;
            if (variableReferenceExpression != null)
            {
                IVariableReference variableReference = variableReferenceExpression.Variable;
                IVariableDeclaration variableDeclaration = variableReference.Resolve();
                TypeHolder thObj = new TypeHolder(GetFullTypeName(variableDeclaration.VariableType), variableDeclaration.Name);
                return thObj;
            }

            IArgumentReferenceExpression argumentReferenceExpression = expr as IArgumentReferenceExpression;
            if (argumentReferenceExpression != null)
            {
                IParameterDeclaration parameterDeclaration = argumentReferenceExpression.Parameter.Resolve();
                TypeHolder thObj = new TypeHolder(GetFullTypeName(parameterDeclaration.ParameterType), parameterDeclaration.Name);
                return thObj;
            }

            ILiteralExpression literalExpression = expr as ILiteralExpression;
            if (literalExpression != null)
            {
                if (literalExpression.Value == null)
                {
                    return new TypeHolder("nil");
                }
                else if (literalExpression.Value is char)
				{
                    return new TypeHolder("char", "\"" + literalExpression.Value.ToString() + "\"");					
				}
                else if (literalExpression.Value is string)
				{
                    return new TypeHolder("string", "\"" + literalExpression.Value.ToString() + "\"");										
				}
                else if (literalExpression.Value is byte)
				{
                    return new TypeHolder("byte", literalExpression.Value.ToString());					
				}
                else if (literalExpression.Value is sbyte)
                {
                    return new TypeHolder("sbyte", literalExpression.Value.ToString());
				}
                else if (literalExpression.Value is Int16)
				{
                    return new TypeHolder("System.Int16", literalExpression.Value.ToString());
				}
                else if (literalExpression.Value is UInt16)
				{
                    return new TypeHolder("System.UInt16", literalExpression.Value.ToString());
				}
                else if (literalExpression.Value is Int32)
				{
                    return new TypeHolder("System.Int32", literalExpression.Value.ToString());
				}
                else if (literalExpression.Value is UInt32)
				{
                    return new TypeHolder("System.UInt32", literalExpression.Value.ToString());
				}
                else if (literalExpression.Value is Int64)
				{
                    return new TypeHolder("System.Int64", literalExpression.Value.ToString());
				}
                else if (literalExpression.Value is UInt64)
				{
                    return new TypeHolder("System.UInt64", literalExpression.Value.ToString());
				}
                else if (literalExpression.Value is Single)
				{
                    return new TypeHolder("System.Single", literalExpression.Value.ToString());
				}
                else if (literalExpression.Value is Double)
				{
                    return new TypeHolder("System.Double", literalExpression.Value.ToString());
				}
                else if (literalExpression.Value is Decimal)
				{
                    return new TypeHolder("System.Decimal", literalExpression.Value.ToString());
				}
                else if (literalExpression.Value is bool)
				{
                    return new TypeHolder("bool", literalExpression.Value.ToString());
				}
                else if (literalExpression.Value is byte[])
                {
                    return new TypeHolder("byte[]", literalExpression.Value.ToString());
                }
                else
                {
                    return new TypeHolder("UNKNOWN");
                }
            }

            ITypeOfExpression typeOfExpression = expr as ITypeOfExpression;
            if (typeOfExpression != null)
            {
                return new TypeHolder(GetFullTypeName(typeOfExpression.Type));
            }

            ITypeReferenceExpression typeReferenceExpression = expr as ITypeReferenceExpression;
            if (typeReferenceExpression != null)
            {
                return new TypeHolder(GetFullTypeName(typeReferenceExpression.Type));                
            }

            IMethodInvokeExpression methodInvokeExpression = expr as IMethodInvokeExpression;
            if (methodInvokeExpression != null)
            {
                IMethodReferenceExpression methodReferenceExpr = methodInvokeExpression.Method as IMethodReferenceExpression;
                IMethodReference methodReference = methodReferenceExpr.Method;
                return new TypeHolder(SeqGenUtil.GetFullTypeName(methodReference.ReturnType.Type));                
            }

            IObjectCreateExpression objectCreateExpression = expr as IObjectCreateExpression;
            if (objectCreateExpression != null)
            {
                return new TypeHolder(SeqGenUtil.GetFullTypeName(objectCreateExpression.Constructor.DeclaringType));
            }

            IThisReferenceExpression thisReferenceExpression = expr as IThisReferenceExpression;
            if (thisReferenceExpression != null)
            {
                return new TypeHolder("self");
            }

            IFieldReferenceExpression fieldReferenceExpression = expr as IFieldReferenceExpression;
            if (fieldReferenceExpression != null)
            {
                TypeHolder typObj = new TypeHolder(SeqGenUtil.GetFullTypeName(fieldReferenceExpression.Field.FieldType));
                typObj.setVarName(fieldReferenceExpression.ToString());
                return typObj;
            }

            IPropertyReferenceExpression propertyReferenceExpression = expr as IPropertyReferenceExpression;
            if (propertyReferenceExpression != null)
            {
                IPropertyReference iprObj = propertyReferenceExpression.Property;
                IExpression receiverObj = propertyReferenceExpression.Target;
                IPropertyDeclaration ipdObj = iprObj.Resolve();
                TypeHolder typObj = new TypeHolder(ipdObj.PropertyType.ToString());
                typObj.setCompleteExprName(typObj.getCompleteExprName() + "." + ipdObj.Name);
                return typObj;                
            }

            IVariableDeclarationExpression variableDeclarationExpression = expr as IVariableDeclarationExpression;
            if (variableDeclarationExpression != null)
            {
                IVariableDeclaration ivdObj = variableDeclarationExpression.Variable;
                TypeHolder typObj = new TypeHolder(SeqGenUtil.GetFullTypeName(ivdObj.VariableType), ivdObj.Name);
                return typObj;
            }
            
            //TODO: To handle other cases
            /*           
            
            ITypeOfExpression typeOfExpression = expression as ITypeOfExpression;
            if (typeOfExpression != null)
            {
                this.WriteTypeOfExpression(typeOfExpression, formatter);
                return;
            }

            INamedArgumentExpression namedArgumentExpression = expression as INamedArgumentExpression;
            if (namedArgumentExpression != null)
            {
                this.WriteNamedArgumentExpression(namedArgumentExpression, formatter);
                return;
            }

            ITypeReferenceExpression typeReferenceExpression = expression as ITypeReferenceExpression;
            if (typeReferenceExpression != null)
            {
                this.WriteTypeReferenceExpression(typeReferenceExpression, formatter);
                return;
            }

            IFieldReferenceExpression fieldReferenceExpression = expression as IFieldReferenceExpression;
            if (fieldReferenceExpression != null)
            {
                this.WriteFieldReferenceExpression(fieldReferenceExpression, formatter);
                return;
            }

            IEventReferenceExpression eventReferenceExpression = expression as IEventReferenceExpression;
            if (eventReferenceExpression != null)
            {
                this.WriteEventReferenceExpression(eventReferenceExpression, formatter);
                return;
            }


            IMethodReferenceExpression methodReferenceExpression = expression as IMethodReferenceExpression;
            if (methodReferenceExpression != null)
            {
                this.WriteMethodReferenceExpression(methodReferenceExpression, formatter);
                return;
            }

            IArgumentListExpression argumentListExpression = expression as IArgumentListExpression;
            if (argumentListExpression != null)
            {
                this.WriteArgumentListExpression(argumentListExpression, formatter);
                return;
            }

            IStackAllocateExpression stackAllocateExpression = expression as IStackAllocateExpression;
            if (stackAllocateExpression != null)
            {
                this.WriteStackAllocateExpression(stackAllocateExpression, formatter);
                return;
            }

            IPropertyReferenceExpression propertyReferenceExpression = expression as IPropertyReferenceExpression;
            if (propertyReferenceExpression != null)
            {
                this.WritePropertyReferenceExpression(propertyReferenceExpression, formatter);
                return;
            }

            IArrayCreateExpression arrayCreateExpression = expression as IArrayCreateExpression;
            if (arrayCreateExpression != null)
            {
                this.WriteArrayCreateExpression(arrayCreateExpression, formatter);
                return;
            }

            IArrayInitializerExpression arrayInitializerExpression = expression as IArrayInitializerExpression;
            if (arrayInitializerExpression != null)
            {
                this.WriteArrayInitializerExpression(arrayInitializerExpression, formatter);
                return;
            }


            IBaseReferenceExpression baseReferenceExpression = expression as IBaseReferenceExpression;
            if (baseReferenceExpression != null)
            {
                this.WriteBaseReferenceExpression(baseReferenceExpression, formatter);
                return;
            }

            IUnaryExpression unaryExpression = expression as IUnaryExpression;
            if (unaryExpression != null)
            {
                this.WriteUnaryExpression(unaryExpression, formatter);
                return;
            }

            IBinaryExpression binaryExpression = expression as IBinaryExpression;
            if (binaryExpression != null)
            {
                this.WriteBinaryExpression(binaryExpression, formatter);
                return;
            }

            ITryCastExpression tryCastExpression = expression as ITryCastExpression;
            if (tryCastExpression != null)
            {
                this.WriteTryCastExpression(tryCastExpression, formatter);
                return;
            }

            ICanCastExpression canCastExpression = expression as ICanCastExpression;
            if (canCastExpression != null)
            {
                this.WriteCanCastExpression(canCastExpression, formatter);
                return;
            }

            ICastExpression castExpression = expression as ICastExpression;
            if (castExpression != null)
            {
                this.WriteCastExpression(castExpression, formatter);
                return;
            }

            IConditionExpression conditionExpression = expression as IConditionExpression;
            if (conditionExpression != null)
            {
                this.WriteConditionExpression(conditionExpression, formatter);
                return;
            }

            IDelegateCreateExpression delegateCreateExpression = expression as IDelegateCreateExpression;
            if (delegateCreateExpression != null)
            {
                this.WriteDelegateCreateExpression(delegateCreateExpression, formatter);
                return;
            }

            IArgumentReferenceExpression argumentReferenceExpression = expression as IArgumentReferenceExpression;
            if (argumentReferenceExpression != null)
            {
                this.WriteArgumentReferenceExpression(argumentReferenceExpression, formatter);
                return;
            }

            IVariableDeclarationExpression variableDeclarationExpression = expression as IVariableDeclarationExpression;
            if (variableDeclarationExpression != null)
            {
                this.WriteVariableDeclarationExpression(variableDeclarationExpression, formatter);
                return;
            }

            IVariableReferenceExpression variableReferenceExpression = expression as IVariableReferenceExpression;
            if (variableReferenceExpression != null)
            {
                this.WriteVariableReferenceExpression(variableReferenceExpression, formatter);
                return;
            }

            IPropertyIndexerExpression propertyIndexerExpression = expression as IPropertyIndexerExpression;
            if (propertyIndexerExpression != null)
            {
                this.WritePropertyIndexerExpression(propertyIndexerExpression, formatter);
                return;
            }

            IArrayIndexerExpression arrayIndexerExpression = expression as IArrayIndexerExpression;
            if (arrayIndexerExpression != null)
            {
                this.WriteArrayIndexerExpression(arrayIndexerExpression, formatter);
                return;
            }

            IMethodInvokeExpression methodInvokeExpression = expression as IMethodInvokeExpression;
            if (methodInvokeExpression != null)
            {
                this.WriteMethodInvokeExpression(methodInvokeExpression, formatter);
                return;
            }

            IDelegateInvokeExpression delegateInvokeExpression = expression as IDelegateInvokeExpression;
            if (delegateInvokeExpression != null)
            {
                this.WriteDelegateInvokeExpression(delegateInvokeExpression, formatter);
                return;
            }

            IObjectCreateExpression objectCreateExpression = expression as IObjectCreateExpression;
            if (objectCreateExpression != null)
            {
                this.WriteObjectCreateExpression(objectCreateExpression, formatter);
                return;
            }

            IThisReferenceExpression thisReferenceExpression = expression as IThisReferenceExpression;
            if (thisReferenceExpression != null)
            {
                this.WriteThisReferenceExpression(thisReferenceExpression, formatter);
                return;
            }

            IAddressOfExpression addressOfExpression = expression as IAddressOfExpression;
            if (addressOfExpression != null)
            {
                this.WriteAddressOfExpression(addressOfExpression, formatter);
                return;
            }

            IAddressReferenceExpression addressReferenceExpression = expression as IAddressReferenceExpression;
            if (addressReferenceExpression != null)
            {
                this.WriteAddressReferenceExpression(addressReferenceExpression, formatter);
                return;
            }

            IAddressOutExpression addressOutExpression = expression as IAddressOutExpression;
            if (addressOutExpression != null)
            {
                this.WriteAddressOutExpression(addressOutExpression, formatter);
                return;
            }

            IAddressDereferenceExpression addressDereferenceExpression = expression as IAddressDereferenceExpression;
            if (addressDereferenceExpression != null)
            {
                this.WriteAddressDereferenceExpression(addressDereferenceExpression, formatter);
                return;
            }

            ISizeOfExpression sizeOfExpression = expression as ISizeOfExpression;
            if (sizeOfExpression != null)
            {
                this.WriteSizeOfExpression(sizeOfExpression, formatter);
                return;
            }

            IStatementExpression statementExpression = expression as IStatementExpression;
            if (statementExpression != null)
            {
                this.WriteStatementExpression(statementExpression, formatter);
                return;
            }

            ITypeOfTypedReferenceExpression typeOfTypedReferenceExpression = expression as ITypeOfTypedReferenceExpression;
            if (typeOfTypedReferenceExpression != null)
            {
                this.WriteTypeOfTypedReferenceExpression(typeOfTypedReferenceExpression, formatter);
                return;
            }

            IGenericDefaultExpression genericDefaultExpression = expression as IGenericDefaultExpression;
            if (genericDefaultExpression != null)
            {
                this.WriteGenericDefaultExpression(genericDefaultExpression, formatter);
                return;
            }

            ISnippetExpression snippetExpression = expression as ISnippetExpression;
            if (snippetExpression != null)
            {
                this.WriteSnippetExpression(snippetExpression, formatter);
                return;
            }


            throw new ArgumentException("Invalid expression type.", "expression");*/
            return new TypeHolder("UNKNOWN");
        }


        public static String GetFullTypeName(IType typObj)
        {
            if (typObj is ITypeReference)
            {
                ITypeReference typRef = typObj as ITypeReference;
                return typRef.Namespace.ToString() + "." + typRef.Name.ToString();
            }

            return "UNKNOWN";
        }
        
        public static bool IsPrimitiveType(String typeName)
        {
            if (SeqGenerator.primitiveDataTypes.Contains(typeName))
            {
                return true;
            }
            return false;
        }

        //Function that gets several possible return types based on the location
        //of the usage. These heuristics are similar to the ones used in our PARSEWeb
        public static TypeHolder getReturnType(IExpression exprNode)
        {
            return null;
        }
    }    
}
